Most computer systems execute under the control of a multiprocessing operating system. A multiprocessing operating system is designed to control the concurrent execution of multiple computer programs. Such operating systems typically allow one program to execute for a while, and then allow another computer program to execute. Before each computer program is allowed to execute, the operating system initializes the system resources (e.g., CPU registers) to their state when that computer program last executed and, if necessary, loads the computer program back into memory. Thus, when the computer program is allowed to execute again, its execution continues where it left off Each executing computer program is generally referred to as a “process,” and the state of the computer system when that computer program executes is referred to as the “context” of the process.
Computer programs that are executing concurrently may have the need to communicate with each other. A computer application may be implemented as multiple cooperating computer programs that each execute as separate processes. One computer program may request another computer program to perform a certain function on its behalf. Each time the operating system allows a different computer program to execute, the operating system performs what is referred to as a process context switch. The context switch involves saving the current state of the executing process so that it can continue execution at a later time, and restoring the previous state of the computer program to be executed. The saving and restoring of the contexts may place an unacceptable overhead on the computer system. To avoid the overhead of process context switching, some operating systems have implemented the concept of a lightweight process or a thread. Within a single process, the operating system may allow several threads to execute concurrently. Each thread in a process shares the same address space (i.e., code and data), but each thread has its own program counter and other register values. The operating system allows each thread in the process to execute for a while and then switches control to the next thread. Because the threads share the same address space, the context switching from one thread to another can be accomplished much faster than switching from one process to another. Thus, the concept of multithreading allows many of the advantages of multiprocessing, but without the overhead of process context switches.
Many server computer programs (i.e., servers) that provide services to multiple client computer programs (i.e., clients) are implemented using multiple threads. When a client requests the server to perform a service on behalf of the client, the server will create a thread to provide that service for the client. Thus, when multiple clients request services at approximately the same time, the server will be executing many threads. The data that a process accesses is often organized into objects that encapsulate data and code for accessing that data. Because the threads within a process share the same data, access control to the data must be coordinated by the threads. With such a multithreading server, the developer of the server is responsible for coordinating access to the objects so that the processing by one thread will not be interfered with by another thread. For example, the developer may need to ensure that appropriate synchronization mechanism (e.g., locks and semaphores) are defined for each object. A synchronization problem could occur when two threads attempt to access an object concurrently. For example, if both threads A and B need to modify the same object, then a synchronization problem could occur in the following way. When the object provides a method to increment a data member, the method may load the value of the data member into a register, increment the value, and store the value back into the data member. However, if thread A invokes the method, the current value (e.g., 10) of the data member may get loaded into a register and then a thread context switch may occur so that thread B executes. If thread B also invokes the method, the current value (e.g., 10) of the data member would be loaded into a register, incremented (e.g., 11), and stored back into the data member. When thread A again executes, it resumes execution of the method. In this case, the value (i.e., 10) in its register is incremented (e.g., 11) and stored back into the data member. Thus, after execution of the method by both threads the data member will have a value of 11 rather than the expected value of 12. It may take a considerable amount of programming to develop a computer program that avoids various synchronization problems. The synchronization problems also occur when multiple processes, even with only one thread each, access shared memory. Thus, in general the problems occur when any threads, regardless of whether they are in the same or different processes, access objects in shared memory.
To help simplify development of such objects, an apartment model has been developed. According to the apartment model, each object can only be accessed by the single thread. Because each object can only be accessed by a single thread no synchronization problems occur as a result of multiple threads accessing the object concurrently. That is, the single thread can serially process each request to access the object. Thus, when a thread wants to access an object, it requests that the thread for the object perform the function on behalf of the requesting thread. The group of objects that can be accessed by a thread is referred to as an apartment. The apartment model is described in U.S. Pat. Ser. No. 08/381,635, entitled “Method and System for Multithreaded Processing,” which is hereby incorporated by reference. A preferred technique for sending the request from one thread to another is referred to as marshaling and described in U.S. Pat. No. 5,511,197, entitled “Method and System for Network Marshalling of Interface Pointers for Remote Procedure Calls,” which is hereby incorporated by reference.
When a programmer is developing a new object, the programmer needs to make a decision as to whether the object will be accessed only by a single thread or by multiple threads. If the object is to be accessed only by a single thread (i.e., an apartment-threaded object), then the implementation of the object is fairly straightforward because synchronization problems can be avoided by serializing access. Conversely, if the object is to be accessed by multiple threads in a multithreaded apartment (i.e., a free-threaded object), then the appropriate synchronization management mechanisms need to be developed. Unfortunately, apartment-threaded objects cannot be accessed by a client developed to access objects from any thread. When free-threaded objects are being used, a client is developed to simply pass pointers to the objects from one thread to another thread. The receiving thread would access the object directly, regardless of which thread created the object. Thus, if such a client loaded an apartment-threaded object, synchronization problems may occur as multiple threads accessed the object concurrently. Consequently, each class of object has an associated thread type. The thread type of the class can be either apartment-threaded or free-threaded. Two other thread types can be defined: single-threaded objects and both-threaded objects. A single-threaded object (referred to as a main-threaded object in the following) is an apartment-threaded object that can only be loaded into the apartment for the main thread of the process. A both-threaded object is compatible with both the apartment model and the free-threading model and thus can be loaded by a process that uses either model.
Many objects have been developed, in accordance with the Microsoft Component Object Model (“COM”), that are apartment-threaded, free-threaded, main-threaded, or both-threaded. COM is described in “Inside OLE, Second Edition,” by Kraig Brockschmidt and published by Microsoft Press, which is hereby incorporated by reference. It would be desirable if these objects could be accessed by programs that were not developed to adhere to COM. For example, programs written in the Java programming language typically do not adhere to COM. However, it would be desirable if Java programs could access COM objects of varying thread types in a manner that is transparent to the Java program. Such accessibility would be especially useful in Web pages developed for the World-Wide Web. Such Web pages often contain Java applets that are executed byte Java virtual machine (“VM”) when the Web page is presented (typically by a Web browser). A Java VM provides a multithreaded execution environment for the Java programming language. These Java applets could take advantage of the functionality of the COM objects that have already been developed. However, a programmer of the Java applet would currently need to develop the applet so that it adheres to the COM. It would be desirable to have a mechanism in which a Java applet could access COM objects without having to be concerned of the COM threading model.